import encrypt from '../encrypt/current'
import enpt from '../encrypt/encrypt'
import config from '../config' // 配置文件
import { getPublicKey } from '../api/auth'
import { msg, isValidKey, hideLoading, showLoading, showModal } from './tools'
import store from '../store'
import { useLogin } from '@/hooks/hooks'
import { usePlatform } from '@/hooks/usePlatform'
import { COOKIES, TOKEN, TOKEN_EXPIRATIONTIME } from './storageTypes'
import Qs from 'qs'
import { SET_TOKEN } from '@/store/types'
import { useClearUserInfo } from '@/hooks/user'

let reqOptions: CustomRequestOptions | null = null
let requestNumber:number = 0
const request = async <T = any>(options: CustomRequestOptions) => {
  reqOptions = JSON.parse(JSON.stringify(options))
  const { url, method, baseUrl = config.uni_app_web_api_url } = options
  if (!options.noLoading) {
    if(requestNumber > 0){
      requestNumber++
    }else{
      requestNumber = 0
      requestNumber++
      showLoading()
    }
    // uni.showLoading('')
  }
  options.url = url.includes('http') ? url : baseUrl + '' + url
  if (options.header) {
    Object.assign(options.header, {
      channel: 'WX_MP',
      'city-code': config.cityCode,
      channelType: usePlatform(),
    })
  } else {
    options.header = {
      channel: 'WX_MP',
      'city-code': config.cityCode,
      channelType: usePlatform(),
    }
  }
  // if (store.state.user.TGT) {
  //   Object.assign(options.header, {
  //     Authorization: `Bearer ${store.state.user.TGT}`,
  //     aid: `${store.state.user.userInfo?.aid || ''}`,
  //   })
  // }

  const pattern:RegExp = new RegExp("/mobile/[^/]*/public/.*",'g');
  const patternOauth2:RegExp = new RegExp("/oauth2/.*",'g');
  if((!pattern.test(options.url) || patternOauth2.test(options.url)) && store.state.user.token){
      Object.assign(options.header, {
        Authorization: `Bearer ${store.state.user.token}`,
      })
  }
  const commonHeader = {
    'eshimin-version': '1.0.0',
    platform: 4,
    channel: 4,
    cityCode: config.cityCode,
    cookie: uni.getStorageSync(COOKIES) || '',
  }

  Object.assign(options.header, commonHeader)

  // 判断请求是否加密
  let publicKey = ''
  // 判断是否需要加密 需要加密获取公钥
  if (!options.notEncrypt) {
    const currentTime = new Date().getTime()
    const publicKeyExpireTime = uni.getStorageSync('publicKeyExpireTime') || 0
    console.group(options.url,'请求数据')
    console.log(options.data)
    console.groupEnd()
    if (parseInt(publicKeyExpireTime) < currentTime) {
      publicKey = await getPublicKeyAsync()
    }
    if (!publicKey) {
      publicKey = uni.getStorageSync('publicKey') || ''
    }
    options.key = enpt.string_key
    options.header['x-key'] = encrypt.RSAencrypt(publicKey, options.key || '')
    options.header['Content-Type'] = 'application/json'
    if((method as string) == 'GET'){
      trimNullFiled(options.data)
      const parseData = Qs.stringify(options.data)
      options.data = {
        x_enc:encrypt.AESencrypt(parseData,options.key || '')
      }
    }else{
      options.data = encrypt.AESencrypt(JSON.stringify(options.data),options.key || '')
    }
  }

  return new Promise<ResponseOptions<T>['data']>((resolve, reject) => {
    const defaultOptions = {
      success: async (apiRes: UniApp.RequestSuccessCallbackResult) => {
        if (apiRes.header['Set-Cookie']) {
          uni.setStorageSync('cookie', apiRes.header['Set-Cookie'])
        }
        if (apiRes.header['Authorization']) {
          uni.setStorageSync(TOKEN, apiRes.header['Authorization'])
          uni.setStorageSync(TOKEN_EXPIRATIONTIME, apiRes.header['expirationTime'])
          store.commit(`user/${SET_TOKEN}`, apiRes.header['Authorization'])
        }
        const { data } = apiRes
        const res = data as AnyObject
        let result = null
          if (res && res.code) {
            const resData = await resolveErrorRes(res)
            resolve(resData as ResponseOptions['data'])
          }
          if(res.code == 0){
            uni.showModal({
              title: '温馨提示',
              content: (res && res.msg) || '请求异常',
              showCancel:false,
              success:(_res) => {
                return reject(res)
              }
            })
          }
          if (res && res.data && !options.notEncrypt) {
            result = encrypt.decrypt(res.data, options.key as string)
            res.data = result
            console.group(options.url,'返回结果')
            console.log(res)
            console.groupEnd()
            await resolveErrorRes(res)
            resolve(result)
          }
      },
      fail: (err: GeneralCallbackResult) => {
        reject(requestErrorResponse())
      },
      complete:(comp:any) => {
        requestNumber--
        if(requestNumber <= 0){
          hideLoading()
        }
        handleStatusCode(comp.statusCode)

      }
    }
    uni.request({ ...defaultOptions, ...options })
  })
}

const trimNullFiled = (obj: any) => {
  if (Array.isArray(obj)) {
    for (let i = 0; i < obj.length; i++) {
      trimNullFiled(obj[i])
    }
  } else {
    obj && Object.keys(obj).forEach((key) => {
      if ( obj[key] && obj[key] == null) {
        delete obj[key]
      }
      if (typeof obj[key] == 'object') {
        trimNullFiled(obj[key])
      }
    })
  }
}

const getPublicKeyAsync = async () => {
  try {
    const { data } = await getPublicKey()
    uni.setStorageSync('publicKeyExpireTime', data.expireTime)
    uni.setStorageSync('publicKey', data.publicKey)

    return Promise.resolve(data.publicKey)
  } catch (error) {
    return Promise.resolve(requestErrorResponse())
  }
}

const noAuth = () => {
  uni.showToast({
    icon: 'error',
    title: '未登录，请先登录',
    mask: true,
    // #ifdef MP-WEIXIN
    duration: 2000,
    // #endif
    success: () => {
      useLogin()
    },
  })
}

const handleStatusCode = (status:number) => {
  switch (status){
    case 401:
      // #ifndef H5
      showModal({
        content:'登录已过期，请重新登录',
        showCancel:true,confirmText:'前往登录',
        success:(res) =>{
          if(res.confirm){
            useLogin()
          }
          if(res.cancel){
            useClearUserInfo()
          }
        }
      })
      // #endif


      // #ifdef H5
      showModal({
        content:'登录已过期，请退出后重新进入',
        showCancel:true,confirmText:'退出',
        success:(res) =>{
          if(res.confirm){
            useClearUserInfo()
            window.location.replace('/#/')
          }
          if(res.cancel){
            useClearUserInfo()
          }
        }
      })
      // #endif
      break
    case 500:
      msg('服务器异常，请稍后重试！')
      break
    case 502:
      msg('服务器异常，请稍后重试！')
      break
    case 503:
      msg('服务器正在重启，请稍后重试！')
      break
  }
}

const requestErrorResponse = () => ({
  data: null,
  code: 500,
  msg: '请求异常',
  success: false,
})

const clearPublicKey = async () => {
  uni.removeStorageSync('publicKeyExpireTime')
  uni.removeStorageSync('publicKey')
  return true
}

type ResponesData<T> = T extends UniApp.RequestSuccessCallbackResult['data']
  ? T
  : never

// // 处理异常
const resolveErrorRes = async (data: ResponesData<AnyObject>) => {
  if (data && (+data.code === 4001 || +data.code === 401)) {
    // #ifdef H5
    if(data && data.data){
      return (location.href = data.data)
    }else {
      return false
    }
    // #endif
    noAuth()
    return Promise.resolve(requestErrorResponse())
  }

  if (data && +data.code === 5006) {
    await clearPublicKey()

    return request(reqOptions as CustomRequestOptions)
  }
  if (!data || (+data.code !== 2000 && +data.code !== 200)) {
    uni.showModal({
      title: '温馨提示',
      content: (data && data.msg) || '请求异常',
      showCancel:false,
      success:(res) => {
        return Promise.reject(requestErrorResponse())
      }
    })
  }
  if (
    data &&
    (+data.code === 2000 ||
      +data.code === 200 ||
      ~('' + data.code).indexOf('200'))
  ) {
    return Promise.resolve(data as ResponseOptions['data'])
  }

}

export default request
